library Base initializer Init requires optional Constant

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// UnitAlive(unit)
// IsUnitAlive(unit)
// IsUnitDead(unit)
// Msg(string, real)
// Error()
// Debug()
// String2Id(string)
// Id2String(integer)
// GetCommonCaster()
// GetPlayerSelectedAll(player)
// GetPlayerSelectedOne(player)
// RunBoolexpr(boolexpr)
// AddGold(player, unit, integer)
// SendSound(string, real, real)
// UnitKillTarget(unit, unit)
// IssueAngleOrder(unit, string, real)
// IssueAngleOrderById(unit, integer, real)
// IsUnitInvulnerable(unit)
// IsUnitCanAttack(unit)
// IsUnitCommon(unit)
// IsUnitInvisiby(unit)
// EnumDestructablesInRange(real, real, real, code)
// DestroyDestructablesInRange(real, real, real)
// DestroyDestructablesInRect(rect)
//
// AllyPlayer
// CreepPlayer
// Self
// MapLeft
// MapRight
// MapTop
// MapBottom
// TempGroup
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

globals
	private string StringMap = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
	private trigger array TempTrigger
	private integer TempTriggerTop = 0

	private integer array ShiftSet
	private unit CommonCaster
	player AllyPlayer
	player CreepPlayer
	player Self
	real MapLeft
	real MapRight
	real MapTop
	real MapBottom
	group TempGroup

	private real array EnumDestructableX
	private real array EnumDestructableY
	private real array EnumDestructableRadius
	private integer EnumDestructableTop = -1
	private boolexpr FilterEnumDestructablesInRange
endglobals

native UnitAlive takes unit id returns boolean

function IsUnitAlive takes unit u returns boolean
	return GetUnitTypeId(u) != 0 and UnitAlive(u)
endfunction

function IsUnitDead takes unit u returns boolean
	return GetUnitTypeId(u) != 0 and not UnitAlive(u)
endfunction

function GetArrayMaxSize takes nothing returns integer
	return JASS_MAX_ARRAY_SIZE
endfunction

function Msg takes string msg, real time returns nothing
	local integer i = 0
	if time < 0 then
		loop
			call DisplayTextToPlayer(Player(i), 0, 0, msg)
			set i = i + 1
			exitwhen i == bj_MAX_PLAYERS
		endloop
	else
		loop
			call DisplayTimedTextToPlayer(Player(i), 0, 0, time, msg)
			set i = i + 1
			exitwhen i == bj_MAX_PLAYERS
		endloop
	endif
endfunction

function Error takes nothing returns nothing
	call Msg("|cffff0000游戏出现不可预知的BUG，如果你看到这段话，请将录像保存并E-mail至：libla@qq.com|r", 0)
endfunction

function Debug takes string str returns nothing
debug	 call Msg(str, -1)
endfunction

function String2Id takes string str returns integer
	local string sub
	local integer i
	local integer j
	local integer k = IMinBJ(StringLength(str), 4)
	local integer sum = 0
	set i = 0
	loop
		set sub = SubString(str, i, i + 1)
		set j = 0
		loop
			if sub == SubString(StringMap, j, j + 1) then
				set sum = sum * 256 + j + 32
				exitwhen true
			endif
			set j = j + 1
			exitwhen j == 95
		endloop
		set i = i + 1
		exitwhen i == k
	endloop
	return sum
endfunction

function Id2String takes integer id returns string
	local string str = ""
	local integer index
	loop
		exitwhen id == 0
		set index = id - id / 256 * 256
		if index >= 32 and index <= 126 then
			set str = SubString(StringMap, index - 32, index - 31) + str
		else
			set str = " " + str
		endif
		set id = id / 256
	endloop
	return str
endfunction

function BitShift takes integer i returns integer
	if i == 0 then
		return 1
	endif
	if i > GetArrayMaxSize() then
		return R2I(Pow(2, i))
	endif
	if i < 0 then
		return 0
	endif
	if ShiftSet[i] == 0 then
		set ShiftSet[i] = BitShift(i - 1) * 2
	endif
	return ShiftSet[i]
endfunction

static if LIBRARY_Constant then
	function GetCommonCaster takes nothing returns unit
		return CommonCaster
	endfunction
endif

function GetAllyPlayer takes nothing returns player
	return AllyPlayer
endfunction

function GetCreepPlayer takes nothing returns player
	return CreepPlayer
endfunction

private function GetPlayerSelectedAllChild takes player p, group g returns group
	call GroupEnumUnitsSelected(g, p, null)
	return g
endfunction

function GetPlayerSelectedAll takes player p returns group
	return GetPlayerSelectedAllChild(p, CreateGroup())
endfunction

private function GetPlayerSelectedOneChild takes player p, unit u returns unit
	local group g = CreateGroup()
	call GroupEnumUnitsSelected(g, p, null)
	set u = FirstOfGroup(g)
	call DestroyGroup(g)
	set g = null
	return u
endfunction

function GetPlayerSelectedOne takes player p returns unit
	return GetPlayerSelectedOneChild(p, null)
endfunction

function RunBoolexpr takes boolexpr filter returns boolean
	local boolean result
	local integer i = TempTriggerTop
	if TempTrigger[i] == null then
		set TempTrigger[i] = CreateTrigger()
	endif
	set TempTriggerTop = TempTriggerTop + 1
	call TriggerAddCondition(TempTrigger[i], filter)
	set result = TriggerEvaluate(TempTrigger[i])
	call TriggerClearConditions(TempTrigger[i])
	set TempTriggerTop = TempTriggerTop - 1
	return result
endfunction

function RunCode takes code func returns nothing
	local triggeraction action
	local integer i = TempTriggerTop
	if TempTrigger[i] == null then
		set TempTrigger[i] = CreateTrigger()
	endif
	set TempTriggerTop = TempTriggerTop + 1
	set action = TriggerAddAction(TempTrigger[i], func)
	call TriggerExecute(TempTrigger[i])
	call TriggerRemoveAction(TempTrigger[i], action)
	set action = null
	set TempTriggerTop = TempTriggerTop - 1
endfunction

function AddGold takes player p, unit u, integer gold returns nothing
	if gold > 0 then
		call SetPlayerState(p, PLAYER_STATE_GOLD_GATHERED, GetPlayerState(p, PLAYER_STATE_GOLD_GATHERED) + gold)
		call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) + gold)
		if u != null then
			set bj_lastCreatedTextTag = CreateTextTag()
			call SetTextTagText(bj_lastCreatedTextTag, "+" + I2S(gold), 0.024)
			call SetTextTagVelocity(bj_lastCreatedTextTag, .0, .03)
			call SetTextTagColor(bj_lastCreatedTextTag, 255, 220, 0, 255)
			call SetTextTagPosUnit(bj_lastCreatedTextTag, u, 0)
			call SetTextTagPermanent(bj_lastCreatedTextTag, false)
			call SetTextTagFadepoint(bj_lastCreatedTextTag, 1.)
			call SetTextTagLifespan(bj_lastCreatedTextTag, 2.)
			if Self == p then
				call SetTextTagVisibility(bj_lastCreatedTextTag, true)
			else
				call SetTextTagVisibility(bj_lastCreatedTextTag, false)
			endif
		endif
	endif
endfunction

function SendSound takes string path, real x, real y returns nothing
	local sound localsound = CreateSound(path, false, true, true, 10, 10, "")
	call SetSoundVolume(localsound, 127)
	call SetSoundPosition(localsound, x, y, 0)
	call SetSoundDistanceCutoff(localsound, 2000)
	call StartSound(localsound)
	call KillSoundWhenDone(localsound)
	set localsound = null
endfunction

function UnitKillTarget takes unit killer, unit killed returns nothing//杀死单位
	if killer == null then
		static if LIBRARY_Constant then
			set killer = CommonCaster
		else
			return
		endif
	endif
	call UnitRemoveBuffs(killed, true, true)
	call SetUnitInvulnerable(killed, false)
	call UnitDamageTarget(killer, killed, 999999, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
endfunction

function IssueAngleOrder takes unit whichUnit, string order, real angle returns boolean//指定角度发布命令
	local real x = GetUnitX(whichUnit)
	local real y = GetUnitY(whichUnit)
	loop
		set x = x + 100. * Cos(angle)
		set y = y + 100. * Sin(angle)
		exitwhen x < MapLeft or x > MapRight or y < MapTop or y > MapBottom
		if IssuePointOrder(whichUnit, order, x, y) then
			return true
		endif
	endloop
	return false
endfunction

function IssueAngleOrderById takes unit whichUnit, integer order, real angle returns boolean
	local real x = GetUnitX(whichUnit)
	local real y = GetUnitY(whichUnit)
	loop
		set x = x + 100. * Cos(angle)
		set y = y + 100. * Sin(angle)
		exitwhen x < MapLeft or x > MapRight or y < MapTop or y > MapBottom
		if IssuePointOrderById(whichUnit, order, x, y) then
			return true
		endif
	endloop
	return false
endfunction

static if LIBRARY_Constant then
	function IsUnitInvulnerable takes unit u returns boolean//判断无敌
		return not IssueTargetOrderById(CommonCaster, GetInvulnerableTestOrder(), u)
	endfunction

	function IsUnitCanAttack takes unit u returns boolean//判断单位是否可攻击
		return not IsUnitType(u, UNIT_TYPE_ETHEREAL) and not IsUnitInvulnerable(u)
	endfunction
endif

function IsUnitCommon takes unit u returns boolean//判断单位是否是普通单位
	return not IsUnitType(u, UNIT_TYPE_HERO) and not IsUnitType(u, UNIT_TYPE_RESISTANT)
endfunction

function IsUnitInvisiby takes unit u returns boolean//判断单位是否隐形
	return IsUnitInvisible(u, CreepPlayer)
endfunction

function IsPlayerActive takes player p returns boolean
	return GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING
endfunction

private function EnumDestructablesInRangeFilter takes nothing returns boolean
	local real x = EnumDestructableX[EnumDestructableTop] - GetDestructableX(GetFilterDestructable())
	local real y = EnumDestructableY[EnumDestructableTop] - GetDestructableY(GetFilterDestructable())
	return x * x + y * y <= EnumDestructableRadius[EnumDestructableTop]
endfunction

function EnumDestructablesInRange takes real x, real y, real radius, code action returns nothing
	local rect r
    if (radius > 0) then
    	set EnumDestructableTop = EnumDestructableTop + 1
    	set EnumDestructableX[EnumDestructableTop] = x
    	set EnumDestructableY[EnumDestructableTop] = y
    	set EnumDestructableRadius[EnumDestructableTop] = radius * radius
        set r = Rect(x - radius, y - radius, x + radius, y + radius)
        call EnumDestructablesInRect(r, FilterEnumDestructablesInRange, action)
        call RemoveRect(r)
        set r = null
        set EnumDestructableTop = EnumDestructableTop - 1
    endif
endfunction

private function DestroyDestructable takes nothing returns nothing
	call KillDestructable(GetEnumDestructable())
endfunction

function DestroyDestructablesInRange takes real x, real y, real radius returns nothing
	call EnumDestructablesInRange(x, y, radius, function DestroyDestructable)
endfunction

function DestroyDestructablesInRect takes rect r returns nothing
	call EnumDestructablesInRect(r, null, function DestroyDestructable)
endfunction

private function Init takes nothing returns nothing
	set TempGroup = CreateGroup()
	set AllyPlayer = Player(PLAYER_NEUTRAL_PASSIVE)
	set CreepPlayer = Player(PLAYER_NEUTRAL_AGGRESSIVE)
	set Self = GetLocalPlayer()
	call FogModifierStart(CreateFogModifierRect(CreepPlayer, FOG_OF_WAR_VISIBLE, bj_mapInitialPlayableArea, false, true))
	static if LIBRARY_Constant then
		set CommonCaster = CreateUnit(AllyPlayer, GetCasterType(), 0, 0, 0) // 施法单位的ID
		call UnitAddAbility(CommonCaster, GetInvulnerableTestSkill()) // 测试无敌的技能
	endif
	set MapLeft = GetRectMinX(bj_mapInitialPlayableArea)
	set MapTop = GetRectMinY(bj_mapInitialPlayableArea)
	set MapRight = GetRectMaxX(bj_mapInitialPlayableArea)
	set MapBottom = GetRectMaxY(bj_mapInitialPlayableArea)
	set FilterEnumDestructablesInRange = Filter(function EnumDestructablesInRangeFilter)
endfunction

endlibrary